Skip to content

messages.send: auto_verify body-echo (Phase 2 body-verify defense)#39

Merged
mikemolinet merged 1 commit into
mainfrom
feat/messages-auto-verify-echo
May 11, 2026
Merged

messages.send: auto_verify body-echo (Phase 2 body-verify defense)#39
mikemolinet merged 1 commit into
mainfrom
feat/messages-auto-verify-echo

Conversation

@mikemolinet
Copy link
Copy Markdown
Collaborator

Summary

Layer 2 of the body-verify defense-in-depth (Mike directive 2026-05-11). Sibling to Layer 3 shipped in cueapi-cli #51 + cue-mac-app helpers. Closes the caller-side body-mutation bug class for Python SDK callers.

Design Dock: cue-message-silent-corruption-substrate-design-2026-05-11.

What

  • BodyVerifyMismatchError new exception in cueapi.exceptions with sent_body, received_body, first_divergence_byte, message_id attributes for programmatic recovery.
  • first_divergence_byte(a, b) pure helper — cross-SDK reusable.
  • MessagesResource.send(auto_verify=True) new kwarg, default ON. Adds X-CueAPI-Verify-Echo: true request header. SDK diffs response body_received field against sent body + raises BodyVerifyMismatchError on drift. Opt-out via auto_verify=False.

Backward-compatibility

  • Pre-Layer-1 substrate (no body_received in response): SDK silently no-ops. Existing callers see no behavior change.
  • Once Layer 1 ships (cueapi-primary's lane): substrate echoes back; verify auto-engages.
  • Opt-out preserves pre-Phase-2 wire shape.

Test plan

  • 11 new tests pin all invariants (header default-on, opt-out, byte-identical returns, mismatch raises, backward-compat no-op, helper)
  • 4 existing tests updated to expect default-on header
  • 22 messages_resource tests pass
  • 129 of 130 full-suite tests pass (1 xfailed; 19 pre-existing ERRORs in test_cues.py from api_key env — unrelated)

Defense-in-depth status

  • Layer 1 (substrate X-CueAPI-Verify-Echo header → response body_received echo): cueapi-primary's lane, in flight ETA 1-3d
  • Layer 2 (SDK auto-verify): THIS PR (cueapi-python) + parallel ports to cueapi-cli + cueapi-action
  • Layer 3 (force-file mode in CLI): SHIPPED cueapi-cli #51 + cue-mac-app b892613
  • Layer 4 (docs lead with file-payload pattern): joint with cue-pm, ships last

🤖 Generated with Claude Code

Mike body-verify directive 2026-05-11 — Layer 2 SDK auto-verify
across cueapi-python. Sibling to Layer 3 force-file mode shipped in
cueapi-cli #51 + cue-mac-app commit b892613.

Bug class (caller-side shell expansion silently mutating body content
BEFORE the SDK receives it): cueapi-cli #51 closed the CLI side via
force-file mode. cueapi-python callers (notebook / script / hosted
agent runtime) hit the same class via f-strings + os.popen + format()
+ similar Python-level mutation hidden under "literal string".
Auto-verify catches it server-side via echo-back, raising
BodyVerifyMismatchError instead of silent corruption.

Changes:

cueapi/exceptions.py:
- BodyVerifyMismatchError(CueAPIError) — new exception with sent_body,
  received_body, first_divergence_byte, message_id attributes.
- first_divergence_byte(a, b) — pure helper returning byte index of
  first differing position. -1 when one is a proper prefix of the
  other (length mismatch; caller distinguishes via len()). Cross-SDK
  re-usable.

cueapi/resources/messages.py:
- send(auto_verify=True) — new kwarg, default ON per CTO concur
  (Q-C4). Adds X-CueAPI-Verify-Echo: true header. After 201, checks
  response.get("body_received") against sent body; raises
  BodyVerifyMismatchError on mismatch.
- Backward-compat: when substrate omits body_received field (pre-
  Layer-1 deploy), SDK silently no-ops. Existing callers see no
  behavior change until both Layer 1 + Layer 2 are deployed.
- Opt-out: auto_verify=False omits header + skips check.

tests/test_messages_resource.py:
- 11 new tests pin:
  - Default auto_verify=True adds header
  - Opt-out omits header
  - Byte-identical response returns normally
  - Mismatched response raises BodyVerifyMismatchError with attributes
  - Substrate omits echo field → no raise (backward-compat)
  - Opt-out skips verify even if substrate echoes
  - first_divergence_byte: equal, prefix, first-char, middle, realistic
    metachar-substitution scenario
- 4 existing tests updated to expect default-on header.
- All 22 messages tests pass.

Phase 2 substrate field name `body_received` documented as locked
during joint CMA + cueapi-primary design (design Dock workspace
cue-message-silent-corruption-substrate-design-2026-05-11). If
Layer 1 ships under a different name, update _VERIFY_ECHO_FIELD
constant (single 1-line change).

CHANGELOG entry under [Unreleased].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@govindkavaturi-art govindkavaturi-art enabled auto-merge (squash) May 11, 2026 22:52
@mikemolinet mikemolinet merged commit b3743d3 into main May 11, 2026
4 checks passed
@mikemolinet mikemolinet deleted the feat/messages-auto-verify-echo branch May 11, 2026 22:52
mikemolinet added a commit that referenced this pull request May 11, 2026
…wire-shape) (#40)

Hotfix for cueapi-python PR #39. Empirically verified 2026-05-11 ~23:17Z
via direct curl probe: substrate's X-CueAPI-Verify-Echo response
includes ``body_received`` as the PARSED request body (a dict with
to/body/subject/priority/etc fields), NOT a flat string per the
original spec wording.

Before fix: SDK compared ``response["body_received"]`` (dict) against
``body`` (str) — type mismatch → ALWAYS raised BodyVerifyMismatchError
on every default-auto-verify send. Regression in PR #39.

After fix: SDK extracts ``body_received.body`` (str) and compares
against sent body (str). Includes defensive isinstance check that
falls through gracefully if a future substrate rev flattens the echo
back to a string.

Tests updated to use the dict shape; 1 new defensive test pins the
backward-flat-string path. 23 of 23 messages tests pass.

This hotfix is unblocking — every auto-verify send was broken in main
until this lands.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mikemolinet added a commit that referenced this pull request May 12, 2026
…dy coverage, bump audit to 2026-05-12 (#43)

Manifest was dated 2026-05-07 and missing the PR-1b event-emit endpoint
coverage (4 endpoints) plus the body-verify Phase 2 + inline_body
extensions that shipped 2026-05-09 → 2026-05-12. Brings the manifest
back in sync with SDK head.

Endpoints added to `endpoints_covered`:
- POST /v1/agents/{ref}/subscriptions (subscriptions_create, PR #38;
  inline_body kwarg in PR #42 / cueapi #791 Item 1)
- GET /v1/agents/{ref}/subscriptions (subscriptions_list, PR #38)
- DELETE /v1/agents/{ref}/subscriptions/{sub_id} (subscriptions_delete,
  PR #38)
- GET /v1/agents/{ref}/events (events_pull, PR #38)

Updates to existing entries:
- POST /v1/messages — added auto_verify body-verify Phase 2 (PR #39 +
  #40, cueapi/cueapi #795 + #798 parity)
- POST /v1/cues/{id}/fire — note that #33 shipped (was "in-flight")
- GET /v1/agents/roster — note that #35 shipped (was "in-flight")
- GET /v1/agents/{ref}/presence — note that #35 shipped (was "in-flight")

Replaced `in_flight_ports_2026_05_07` section with
`ports_shipped_2026_05_08_to_2026_05_12` (now-resolved entries) plus a
near-empty `ports_in_flight_2026_05_12` placeholder for future ports.

Backlog row: cmp1vukmc.

Out of scope:
- `model_drift` section walk-through (Cue/Execution/Worker missing
  fields) — PR #31 just landed the Execution + Worker + Agent +
  Message additive models; a fuller `model_drift` refresh deserves a
  separate audit pass against the now-shipped models to figure out
  what's still drifting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant